package com.practice.J2E.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Header;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
import com.practice.J2E.constant.GlobalConstant;
import com.practice.J2E.util.dto.ExcelHandler;
import com.practice.J2E.util.dto.ExportField;
import com.practice.J2E.util.dto.TestExport;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;


import java.io.*;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author kayn
 * @create 2023-04-11 下午 05:40
 */
public class EasyExcelUtils {

    private static final Logger logger = LoggerFactory.getLogger(EasyExcelUtils.class);

    public static void main(String[] args) throws IOException {

        List<TestExport> dataList = CollUtil.newArrayList(
                TestExport.init("OMS001", "这是一个主单号!"),
                TestExport.init("OMS002", "这是另一个主单号!")
        );

        //测试获取文件输入流
//        InputStream exportInputStream = getExportInputStream(dataList, TestExport.class);

        String exportBase64 = getExportBase64(dataList, TestExport.class,"测试页名");

        byte[] bytes = Base64.getDecoder().decode(exportBase64);

        InputStream exportInputStream = new ByteArrayInputStream(bytes);

        BufferedInputStream in = new BufferedInputStream(exportInputStream);

        File file = FileUtil.newFile("C:\\Users\\1\\Desktop\\1.xlsx");

        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));

        int len;

        byte[] b = new byte[1024];

        while((len = in.read(b)) != -1){

            out.write(b, 0, len);

        }

        in.close();

        out.close();

    }

    /**
     * 统一导出,通过HttpServletResponse输出文件流
     * @param exportFieldList 导出字段
     * @param exportDataList 导出数据
     * @param fileName 文件名
     * @param <T> 导出数据泛型
     * @throws Exception
     */
    public static <T> void export(List<ExportField> exportFieldList, List<T> exportDataList, String fileName) throws Exception {

        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        Assert.notNull(servletRequestAttributes, "RequestAttributes不能为null");

        HttpServletResponse response = servletRequestAttributes.getResponse();

        Assert.notNull(response, "Response不能为null");

        export(exportFieldList,exportDataList,fileName,response);

    }

        /**
         * 统一导出,通过HttpServletResponse输出文件流
         * @param exportFieldList 导出字段
         * @param exportDataList 导出数据
         * @param fileName 文件名
         * @param response HttpServletResponse
         * @param <T> 导出数据泛型
         * @throws Exception
         */
    public static <T> void export(List<ExportField> exportFieldList, List<T> exportDataList, String fileName, HttpServletResponse response) throws Exception {

        //设置返回头信息
        response.setHeader(Header.CONTENT_DISPOSITION.getValue(), GlobalConstant.ATTACHMENT_FILENAME + URLEncoder.encode(fileName, GlobalConstant.UTF_8) + StrUtil.DOT + GlobalConstant.XLSX);
        response.setContentType(GlobalConstant.MULTIPART_FORM_DATA);
        response.setCharacterEncoding(GlobalConstant.UTF_8);

        //获取字段列名顺序
        List<String> headList = exportFieldList.stream().map(ExportField::getFieldName).collect(Collectors.toList());

        //字段属性名顺序
        List<String> codeList = exportFieldList.stream().map(ExportField::getFieldCode).collect(Collectors.toList());

        //获取内容顺序设定
        List<Object> dataList = new ArrayList<>(exportDataList);

        //将流刷新到浏览器下载
        ServletOutputStream out = response.getOutputStream();

        //导出
        EasyExcel.write(out)
                .needHead(true)
                .head(getHead(headList))
                .excelType(ExcelTypeEnum.XLSX)
                .registerWriteHandler(new ExcelHandler())
                .registerWriteHandler(ExcelHandler.getStyleStrategy())
                .registerWriteHandler(new SimpleRowHeightStyleStrategy((short)40,(short)20))
                .autoCloseStream(Boolean.FALSE)
                .sheet(0, "sheet1")
                .doWrite(getDataList(dataList,codeList));
    }

    /**
     * 统一获取导出文件base64编码
     * @param dataList
     * @param tClass
     * @param <T> 导出数据类型
     * @return base64
     */
    public static <T> String getExportBase64(List<T> dataList,Class<T> tClass,String sheetName){

        //调用获取方法
        ByteArrayOutputStream outputStream = getExportOutputStream(dataList, tClass,sheetName);

        //转换为字节码
        byte[] bytes = outputStream.toByteArray();

        //将字节码转换为base64编码
        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * 统一获取导出文件base64编码
     * @param dataList
     * @param tClass
     * @param <T> 导出数据类型
     * @return base64
     */
    public static <T> String getExportBase64WithOfficeXlsx(List<T> dataList,Class<T> tClass,String sheetName){

        String exportBase64 = getExportBase64(dataList, tClass, sheetName);

        return "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64," + exportBase64;
    }

    /**
     * 统一获取导出文件输入流
     * @param dataList 导出数据列表
     * @param tClass 导出数据泛型类型
     * @throws Exception
     */
    public static <T> ByteArrayInputStream getExportInputStream(List<T> dataList,Class<T> tClass,String sheetName){

        //调用获取方法
        ByteArrayOutputStream outputStream = getExportOutputStream(dataList, tClass,sheetName);

        //转换为输入流
        return new ByteArrayInputStream(outputStream.toByteArray());
    }

    /**
     * 统一获取导出文件输出流
     * @param dataList 导出数据列表
     * @param tClass 导出数据泛型类型
     * @throws Exception
     */
    public static <T> ByteArrayOutputStream getExportOutputStream(List<T> dataList,Class<T> tClass,String sheetName){

        sheetName = StrUtil.isNotBlank(sheetName) ? sheetName : "sheet1";

        //创建输出流,将excel文件写入
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        //写入
        EasyExcel.write(out)
                .needHead(true)
                .head(tClass)
                .excelType(ExcelTypeEnum.XLSX)
                .registerWriteHandler(new ExcelHandler())
                .registerWriteHandler(ExcelHandler.getStyleStrategy())
                .registerWriteHandler(new SimpleRowHeightStyleStrategy((short)40,(short)20))
                .autoCloseStream(Boolean.FALSE)
                .sheet(0, sheetName)
                .doWrite(dataList);

        return out;
    }

    /**
     * 设置Excel头
     *
     * @param headList Excel头信息(中文，比如 ["订单号","委托客户"] )
     * @return
     */
    public static List<List<String>> getHead(List<String> headList) {

        List<List<String>> list = new ArrayList<>();

        for (String value : headList) {

            List<String> head = new ArrayList<>();

            head.add(value);

            list.add(head);

        }

        return list;

    }

    /**
     * 设置Excel数据信息
     *
     * @param dataList 数据列表
     * @param fileList 列英文名集合，例 ["orderNo","consignorName"]
     * @return
     */
    public static List<List<Object>> getDataList(List<Object> dataList, List<String> fileList) {

        List<List<Object>> list = new ArrayList<>();

        for (Object person : dataList) {

            List<Object> data = new ArrayList<>();

            for (String fieldName : fileList) {

                //通过反射根据需要显示的字段，获取对应的属性值
                data.add(getFieldValue(fieldName, person));

            }

            list.add(data);

        }

        return list;

    }

    /**
     * 根据传入的字段获取对应的get方法，如name,对应的getName方法
     *
     * @param fieldName 字段名
     * @param person    对象
     * @return
     */
    public static Object getFieldValue(String fieldName, Object person) {

        try {

            String firstLetter = fieldName.substring(0, 1).toUpperCase();

            String getter = "get" + firstLetter + fieldName.substring(1);

            Method method = person.getClass().getMethod(getter);

            return method.invoke(person);

        } catch (Exception e) {

            logger.error("使用反射获取对象属性值失败", e);

            return "";

        }

    }
}
